﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using vimApi;
using Alex.Library.Cloud.Libs;
using Alex.Library.Cloud.Libs.Exceptions;
using Alex.Library.Cloud.VMWare.V51.Util;
using Alex.Library.Cloud.VMWare.V51.VMMParameters;
using Alex.Library.Cloud.VMWare.V51.Security;
using Alex.Library.Cloud.VMWare.V51.ObjectReferences;

namespace Alex.Library.Cloud.VMWare.V51
{
    /// <summary>
    /// VMWare vSphere Api Provider
    /// </summary>
    public class VMWareApiProvider : IVMMProvider
    {
        protected const string RESULT_SUCESS = "sucess";
        protected const string RESULT_FAILED = "failure";
        
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="userName">connect to ESX/ESXi Server or vCenter's username</param>
        /// <param name="password">connect to ESX/ESXi Server or vCenter's password</param>
        public VMWareApiProvider(string url, string userName, string password)
        {
            this.Url = url;
            this.PortNumber = 443;
            this.UserName = userName;
            this.Password = password;
        }

        /// <summary>
        /// vSphere Api Provider's Capability
        /// </summary>
        public List<string> Capability
        {
            get
            {
                System.Reflection.MemberInfo[] methods = typeof(IVMMProvider).FindMembers(
                    System.Reflection.MemberTypes.Method, 
                    System.Reflection.BindingFlags.Public, null, null);
                return methods.Where(m => !m.Name.StartsWith("get_") && !m.Name.StartsWith("set_"))
                        .Select(m => m.Name).Distinct().ToList();
            }
        }

        /// <summary>
        /// Api WebService url
        /// </summary>
        public string Url { get; set; }

        /// <summary>
        /// Api WebService port, default is 443
        /// </summary>
        public int PortNumber { get; set; }

        /// <summary>
        /// ESX/ESXi Server or vCenter's username
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        /// ESX/ESXi Server or vCenter's password
        /// </summary>
        public string Password { get; set; }

        /// <summary>
        /// vSphere Api Provider Version
        /// </summary>
        public string Version
        {
            get
            {
                var versions = VersionUtil.GetSupportedVersions(this.Url);
                if (versions != null && versions.Count > 0)
                {
                    return versions.Last();
                }
                return string.Empty;
            }
        }

        /// <summary>
        /// create option values with build options
        /// </summary>
        /// <returns></returns>
        private Dictionary<string, object> CreateOptions()
        {
            return new Dictionary<string, object>
                    {
                        { "url",  this.Url},
                        { "portnumber", this.PortNumber },
                        { "username", this.UserName },
                        { "password", this.Password }
                    };
        }

        /// <summary>
        /// Connect to ESX/ESXi Server or vCenter.
        /// </summary>
        /// <param name="action">doing somthing when connect</param>
        /// <returns></returns>
        public bool Connect(IVMMParameter arg, Action<object> action)
        {
            try
            {
                Dictionary<string, object> optionValues = CreateOptions();
                AppUtil cb = AppUtil.Initialize("Connect", optionValues);
                cb.Connect();
                if (action != null)
                {
                    action(cb);
                }
                cb.DisConnect();
                return true;
            }
            catch (Exception e)
            {
                throw new VMMException("Connect", e);
            }
        }

        /// <summary>
        /// Connect to ESX/ESXi Server or vCenter.
        /// </summary>
        /// <returns></returns>
        public bool Connect(IVMMParameter arg)
        {
            return Connect(arg, null);
        }

        /// <summary>
        /// Creates a Folder, Cluster, Datacenter or standalone host. 
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private bool Create(IVMMParameter arg)
        {
            try
            {
                if (!(arg is CreateParameter))
                {
                    throw new ArgumentException("arg is not CreateParameter type.");
                }
                CreateParameter carg = arg as CreateParameter;
                if (!carg.IsValid())
                {
                    throw new ArgumentException("arg is not valid:" + Environment.NewLine + carg.ErrorMessage);
                }
                List<OptionSpec> userOptions = new List<OptionSpec>();
                userOptions.Add(new OptionSpec("parentName", "String", 1,
                    "Specifies the name of the parent folder", null));
                userOptions.Add(new OptionSpec("itemType", "String", 1,
                    "Host-Standalone|Cluster|Datacenter|Folder", null));
                userOptions.Add(new OptionSpec("itemName", "String", 1,
                    "Name of the item being added: For Host please specify the name of the host machine.",
                    null));

                Dictionary<string, object> optionValues = CreateOptions();
                optionValues.Add("parentName", carg.ParentName);
                optionValues.Add("itemType", carg.ItemType);
                optionValues.Add("itemName", carg.ItemName);
                AppUtil cb = AppUtil.Initialize("Create", userOptions, optionValues);
                cb.Connect();

                #region do create

                string itemType = cb.GetOptionValue("itemType");
                string itemName = cb.GetOptionValue("itemName");
                string parentName = cb.GetOptionValue("parentName");

                ManagedObjectReference fmor = cb.Service.GetDecendentMoRef(null, "Folder", parentName);
                if (fmor == null)
                {
                    throw new VMMException("Parent folder:" + parentName + " not found.");
                }
                bool res = false;
                switch (itemType)
                {
                    case CreateItemType.Folder:
                        res = cb.SvcConnection.Service.CreateFolder(fmor, itemName) != null;
                        break;
                    case CreateItemType.Datacenter:
                        res = cb.SvcConnection.Service.CreateDatacenter(fmor, itemName) != null;
                        break;
                    case CreateItemType.Cluster:
                        res = cb.SvcConnection.Service.CreateCluster(fmor, itemName, new ClusterConfigSpec()) != null;
                        break;
                    case CreateItemType.StandaloneHost:
                        HostConnectSpec hostSpec = new HostConnectSpec();
                        hostSpec.hostName = itemName;
                        hostSpec.userName = carg.OtherProps["HostUserName"].ToString();
                        hostSpec.password = carg.OtherProps["HostPassword"].ToString();
                        hostSpec.port = Convert.ToInt32(carg.OtherProps["HostPort"]);
                        ManagedObjectReference taskmor = cb.SvcConnection.Service
                            .AddStandaloneHost_Task(fmor, hostSpec, true);
                        if (taskmor != null)
                        {
                            string status = cb.Service.WaitForTask(taskmor);
                            res = status == RESULT_SUCESS;
                        }
                        break;
                }

                #endregion

                cb.DisConnect();
                return res;
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Execute an asynchronous task for Create Virtual Machine on ESX/ESXi Server or vCenter.
        /// </summary>
        /// <param name="arg">argument of CreateVM. the Type should be CreateVMParameter.</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException">ArgumentException</exception>
        /// <exception cref="VMMException">VMMException</exception>
        public bool CreateVM(IVMMParameter arg)
        {
            try
            {
                if (!(arg is CreateVMParameter))
                {
                    throw new ArgumentException("arg is not CreateVMParameter type.");
                }
                CreateVMParameter carg = arg as CreateVMParameter;
                if (!carg.IsValid())
                {
                    throw new ArgumentException("arg is not valid:" + Environment.NewLine + carg.ErrorMessage);
                }

                #region create option & values

                List<OptionSpec> userOptions = new List<OptionSpec>();
                userOptions.Add(new OptionSpec("vmname", "String", 1, "Name of the virtual machine", null));
                userOptions.Add(new OptionSpec("datacentername", "String", 1, "Name of the datacenter", null));
                userOptions.Add(new OptionSpec("hostname", "String", 0, "Name of the host", null));
                userOptions.Add(new OptionSpec("guestosid", "String", 0, "Type of Guest OS", null));
                userOptions.Add(new OptionSpec("cpucount", "Integer", 0, "Total CPU Count", "1"));
                userOptions.Add(new OptionSpec("disksize", "Integer", 0, "Size of the Disk", "1024"));
                userOptions.Add(new OptionSpec("memorysize", "Integer", 0, "Size of the Memory in the blocks of 1024 MB", "1024"));
                userOptions.Add(new OptionSpec("datastorename", "String", 0, "Name of the datastore", null));

                Dictionary<string, object> optionValues = CreateOptions();
                optionValues.Add("vmname", carg.VMName);
                optionValues.Add("datacentername", carg.DatacenterName);
                optionValues.Add("hostname", carg.HostName);
                optionValues.Add("guestosid", carg.GuestOsId);
                optionValues.Add("cpucount", carg.CPUCount);
                optionValues.Add("disksize", carg.DiskSize);
                optionValues.Add("memorysize", carg.MemorySize);
                optionValues.Add("datastorename", carg.DataStoreName);

                #endregion

                AppUtil cb = AppUtil.Initialize("VMCreate", userOptions, optionValues);
                cb.Connect();

                #region execute create vm task

                VimService vimSrv = cb.SvcConnection.Service;
                string _dcname = cb.GetOptionValue("datacentername");
                ManagedObjectReference dcmor = cb.Service.GetDecendentMoRef(null, "Datacenter", _dcname);
                if (dcmor == null)
                {
                    throw new VMMException("Datacenter " + _dcname + " not found.");
                }
                ManagedObjectReference hfmor = cb.Service.GetMoRefProp(dcmor, "hostFolder");
                List<ManagedObjectReference> crmors = cb.Service.GetDecendentMoRefs(hfmor, "ComputeResource", null);
                string hostname = cb.GetOptionValue("hostname");
                ManagedObjectReference hostmor = null;
                if (hostname != null)
                {
                    hostmor = cb.Service.GetDecendentMoRef(hfmor, "HostSystem", hostname);
                    if (hostmor == null)
                    {
                        throw new VMMException("Host " + hostname + " not found.");
                    }
                }
                else
                {
                    hostmor = cb.Service.GetFirstDecendentMoRef(dcmor, "HostSystem");
                }
                ManagedObjectReference crmor = null;
                hostname = cb.Service.GetDynamicProperty(hostmor, "name").ToString();
                for (int i = 0; i < crmors.Count; i++)
                {

                    ManagedObjectReference[] hrmors = (ManagedObjectReference[])cb.Service
                        .GetDynamicProperty((ManagedObjectReference)crmors[i], "host");
                    if (hrmors != null && hrmors.Length > 0)
                    {
                        int hrmors_len = hrmors.Length;
                        for (int j = 0; j < hrmors_len; j++)
                        {
                            string hname = cb.Service.GetDynamicProperty(hrmors[j], "name").ToString();
                            if (hname.Equals(hostname))
                            {
                                crmor = (ManagedObjectReference)crmors[i];
                                i = crmors.Count + 1;
                                j = hrmors_len + 1;
                            }
                        }
                    }
                }

                if (crmor == null)
                {
                    throw new VMMException("No Compute Resource Found On Specified Host.");
                }

                ManagedObjectReference resourcePool = cb.Service.GetMoRefProp(crmor, "resourcePool");
                ManagedObjectReference vmFolderMor = cb.Service.GetMoRefProp(dcmor, "vmFolder");

                VMUtils vmUtils = new VMUtils(cb);

                VirtualMachineConfigSpec vmConfigSpec = vmUtils.CreateVmConfigSpec(
                    cb.GetOptionValue("vmname"), cb.GetOptionValue("datastorename"),
                    int.Parse(cb.GetOptionValue("disksize")), crmor, hostmor);

                vmConfigSpec.name = cb.GetOptionValue("vmname");
                vmConfigSpec.annotation = "VirtualMachine Annotation";
                vmConfigSpec.memoryMB = Convert.ToInt64(cb.GetOptionValue("memorysize"));
                vmConfigSpec.memoryMBSpecified = true;
                vmConfigSpec.numCPUs = int.Parse(cb.GetOptionValue("cpucount"));
                vmConfigSpec.numCPUsSpecified = true;
                vmConfigSpec.guestId = cb.GetOptionValue("guestosid");

                ManagedObjectReference taskmor = vimSrv.CreateVM_Task(vmFolderMor, vmConfigSpec,
                    resourcePool, hostmor);

                string res = cb.Service.WaitForTask(taskmor);

                #endregion

                cb.DisConnect();
                return res == RESULT_SUCESS;
            }
            catch (Exception e)
            {
                throw new VMMException("CreateVM", e);
            }
        }

        private bool VMPowerOps(string operation, IVMMParameter arg)
        {
            if (!(arg is VMPowerOpsParameter))
            {
                throw new ArgumentException("arg is not VMPowerOpsParameter type.");
            }
            VMPowerOpsParameter oarg = arg as VMPowerOpsParameter;
            if (!oarg.IsValid())
            {
                throw new ArgumentException("arg is not valid:" + Environment.NewLine + oarg.ErrorMessage);
            }

            List<OptionSpec> userOptions = new List<OptionSpec>();
            userOptions.Add(new OptionSpec("vmname", "String", 1, "Name of Virtual Machine", null));
            userOptions.Add(new OptionSpec("operation", "String", 1,
                "Operation [on|off|suspend|resume|reset|rebootGuest|shutdownGuest|standbyGuest]", null));
            userOptions.Add(new OptionSpec("hostname", "String", 1, "Name of the host system", null));
            Dictionary<string, object> optionValues = CreateOptions();
            optionValues.Add("vmname", oarg.VMName);
            optionValues.Add("operation", operation);
            optionValues.Add("hostname", oarg.HostName);
            AppUtil cb = AppUtil.Initialize("VmPowerOps", userOptions, optionValues);
            cb.Connect();

            #region DoVMPowerOps

            string hostName = cb.GetOptionValue("hostname");
            string vmName = cb.GetOptionValue("vmname");
            ManagedObjectReference vmmor = cb.Service.GetDecendentMoRef(null, "VirtualMachine", vmName);
            if (vmmor == null)
            {
                throw new VMMException("Unable to find VirtualMachine named : " + vmName + " in Inventory.");
            }
            ManagedObjectReference hmor = cb.Service.GetDecendentMoRef(null, "HostSystem", hostName);
            if (hmor == null)
            {
                throw new VMMException("Host " + hostName + " not found.");
            }
            ManagedObjectReference tmor = null;
            bool res = false;
            bool nonTask = false;
            switch (operation)
            {
                case "on":
                case "resume":
                    tmor = cb.SvcConnection.Service.PowerOnVM_Task(vmmor, hmor);
                    break;
                case "off":
                    tmor = cb.SvcConnection.Service.PowerOffVM_Task(vmmor);
                    break;
                case "suspend":
                    tmor = cb.SvcConnection.Service.SuspendVM_Task(vmmor);
                    break;
                case "reset":
                    tmor = cb.SvcConnection.Service.ResetVM_Task(vmmor);
                    break;
                case "rebootGuest":
                    cb.SvcConnection.Service.RebootGuest(vmmor);
                    nonTask = true;
                    break;
                case "shutdownGuest":
                    cb.SvcConnection.Service.ShutdownGuest(vmmor);
                    nonTask = true;
                    break;
                case "standbyGuest":
                    cb.SvcConnection.Service.StandbyGuest(vmmor);
                    nonTask = true;
                    break;
            }

            if (tmor != null)
            {
                object[] result = cb.Service.WaitForValues(
                     tmor, new string[] { "info.state", "info.error" },
                     new string[] { "state" }, // info has a property - state for state of the task
                     new object[][] { new object[] { TaskInfoState.success, TaskInfoState.error } }
                  );
                res = result[0].Equals(TaskInfoState.success);
            }
            else if (nonTask)
            {
                res = true;
            }

            #endregion

            cb.DisConnect();
            return res;
        }

        /// <summary>
        /// power on an virtual machine
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool StartVM(IVMMParameter arg)
        {
            try
            {
                return VMPowerOps("on", arg);
            }
            catch (Exception e)
            {
                throw new VMMException("StartVM", e);
            }
        }

        /// <summary>
        /// shutdown guest os
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool ShutdownGuest(IVMMParameter arg)
        {
            try
            {
                return VMPowerOps("shutdownGuest", arg);
            }
            catch (Exception e)
            {
                throw new VMMException("ShutdownVM", e);
            }
        }

        /// <summary>
        /// reboot guest os.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool RebootGuest(IVMMParameter arg)
        {
            try
            {
                return VMPowerOps("rebootGuest", arg);
            }
            catch (Exception e)
            {
                throw new VMMException("RestartVM", e);
            }
        }

        /// <summary>
        /// reset(foce power state change) an virtual machine.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool ResetVM(IVMMParameter arg)
        {
            try
            {
                return VMPowerOps("reset", arg);
            }
            catch (Exception e)
            {
                throw new VMMException("ResetVM", e);
            }
        }

        /// <summary>
        /// power off(force shutdown) an virtual machine.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool PowerOffVM(IVMMParameter arg)
        {
            try
            {
                return VMPowerOps("off", arg);
            }
            catch (Exception e)
            {
                throw new VMMException("VMPowerOff", e);
            }
        }

        /// <summary>
        /// supend an virtual machine.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool SuspendVM(IVMMParameter arg)
        {
            try
            {
                return VMPowerOps("suspend", arg);
            }
            catch (Exception e)
            {
                throw new VMMException("SupendVM", e);
            }
        }

        /// <summary>
        /// resume an suspend vm.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool ResumeVM(IVMMParameter arg)
        {
            try
            {
                return VMPowerOps("resume", arg);
            }
            catch (Exception e)
            {
                throw new VMMException("ResumeVM", e);
            }
        }

        /// <summary>
        /// supend an virtual machine.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool StandbyGuest(IVMMParameter arg)
        {
            try
            {
                return VMPowerOps("standbyGuest", arg);
            }
            catch (Exception e)
            {
                throw new VMMException("SupendVM", e);
            }
        }

        /// <summary>
        /// Execute an asynchronous task for Clone Virtual Machine.
        /// </summary>
        /// <param name="arg">argument of VMClone. the Type should be VMCloneParameter.</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException">ArgumentException</exception>
        /// <exception cref="VMMException">VMMException</exception>
        public bool CloneVM(IVMMParameter arg)
        {
            try
            {
                if (!(arg is VMCloneParameter))
                {
                    throw new ArgumentException("arg is not VMCloneParameter type.");
                }
                VMCloneParameter carg = arg as VMCloneParameter;
                if (!carg.IsValid())
                {
                    throw new ArgumentException("arg is not valid:" + Environment.NewLine + carg.ErrorMessage);
                }

                List<OptionSpec> userOptions = new List<OptionSpec>();
                userOptions.Add(new OptionSpec("DatacenterName", "String", 1, "Name of the Datacenter", null));
                userOptions.Add(new OptionSpec("vmPath", "String", 1, "A path to the VM inventory, example:Datacentername/vm/vmname", null));
                userOptions.Add(new OptionSpec("CloneName", "String", 1, "Name of the Clone(the new virtual machine name)", null));

                Dictionary<string, object> optionValues = CreateOptions();
                optionValues.Add("DatacenterName", carg.DatacenterName);
                optionValues.Add("vmPath", carg.VMPath);
                optionValues.Add("CloneName", carg.CloneName);

                AppUtil cb = AppUtil.Initialize("VMClone", userOptions, optionValues);
                cb.Connect();
                VimService vimSvc = cb.SvcConnection.Service;
                ServiceContent sc = cb.SvcConnection.ServiceContent;
                string cloneName = cb.GetOptionValue("CloneName");
                string vmPath = cb.GetOptionValue("vmPath");
                string dcName = cb.GetOptionValue("DatacenterName");
                // Find the Datacenter reference by using findByInventoryPath().
                ManagedObjectReference dcmor = vimSvc.FindByInventoryPath(sc.searchIndex, dcName);
                if (dcmor == null)
                {
                    throw new VMMException("The specified datacenter is not found");
                }
                // Find the virtual machine folder for this datacenter.
                ManagedObjectReference vmfmor = (ManagedObjectReference)cb.Service.GetMoRefProp(dcmor, "vmFolder");
                if (vmfmor == null)
                {
                    throw new VMMException("The virtual machine is not found");
                }
                ManagedObjectReference vmmor = vimSvc.FindByInventoryPath(sc.searchIndex, vmPath);
                if (vmmor == null)
                {
                    throw new VMMException("The virtual machine is not found");
                }
                VirtualMachineCloneSpec cloneSpec = new VirtualMachineCloneSpec();
                VirtualMachineRelocateSpec relocSpec = new VirtualMachineRelocateSpec();
                cloneSpec.location = relocSpec;
                cloneSpec.powerOn = false;
                cloneSpec.template = false;
                ManagedObjectReference cloneTask = vimSvc.CloneVM_Task(vmmor, vmfmor, cloneName, cloneSpec);
                string status = cb.Service.WaitForTask(cloneTask);
                cb.DisConnect();
                return status == RESULT_SUCESS;
            }
            catch (Exception e)
            {
                throw new VMMException("VMClone", e);
            }
        }

        /// <summary>
        /// create a user.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool CreateUser(IVMMParameter arg)
        {
            try
            {
                if (!(arg is CreateUserParameter))
                {
                    throw new ArgumentException("arg is not CreateUserParameter type.");
                }
                CreateUserParameter carg = arg as CreateUserParameter;
                if (!carg.IsValid())
                {
                    throw new ArgumentException("arg is not valid:" + Environment.NewLine + carg.ErrorMessage);
                }

                Dictionary<string, object> optionValues = CreateOptions();
                AppUtil cb = AppUtil.Initialize("CreateUser", optionValues);
                cb.Connect();

                #region doing create user

                ManagedObjectReference hostLocalAccountManager =
                    cb.SvcConnection.ServiceContent.accountManager;
                ManagedObjectReference hostAuthorizationManager =
                    cb.SvcConnection.ServiceContent.authorizationManager;

                // Create an user
                HostAccountSpec hostAccountSpec = new HostAccountSpec();
                hostAccountSpec.id = carg.NewUserName;
                hostAccountSpec.password = carg.NewPassword;
                hostAccountSpec.description = "User Description";
                cb.SvcConnection.Service.CreateUser(hostLocalAccountManager, hostAccountSpec);

                ManagedObjectReference rootFolder = cb.SvcConnection.ServiceContent.rootFolder;

                Permission per = new Permission();
                per.group = false;
                per.principal = carg.NewUserName;
                per.roleId = -1;
                per.propagate = true;
                per.entity = rootFolder;

                cb.SvcConnection.Service.SetEntityPermissions(hostAuthorizationManager,
                    rootFolder, new Permission[] { per });

                ICredentialStore csObj = CredentialStoreFactory.CreateCredentialStore();
                bool res = csObj.AddPassword(cb.Server, carg.NewUserName, carg.NewPassword.ToCharArray());

                #endregion

                cb.DisConnect();
                return res;
            }
            catch (Exception e)
            {
                throw new VMMException("CreateUser", e);
            }
        }

        /// <summary>
        /// delete an Managed Entry, Virtual Machine, ClusterComputeResource or folder.
        /// </summary>
        /// <param name="arg">argument of Delete. the Type should be DeleteParameter.</param>
        /// <returns></returns>
        private bool Delete(string actionName, IVMMParameter arg)
        {
            try
            {
                if (!(arg is DeleteParameter))
                {
                    throw new ArgumentException("arg is not DeleteParameter type.");
                }
                DeleteParameter darg = arg as DeleteParameter;
                if (!darg.IsValid())
                {
                    throw new ArgumentException("arg is not valid:" + Environment.NewLine + darg.ErrorMessage);
                }
                List<OptionSpec> userOptions = new List<OptionSpec>();
                userOptions.Add(new OptionSpec("meName", "String", 1,
                        "Virtual Machine|ClusterComputeResource|folder", null));
                Dictionary<string, object> optionValues = CreateOptions();
                optionValues.Add("meName", darg.MeName);
                AppUtil cb = AppUtil.Initialize("Delete", userOptions, optionValues);
                cb.Connect();

                #region do delete

                bool res = false;

                ManagedObjectReference memor = cb.Service.GetDecendentMoRef(null, "ManagedEntity", darg.MeName);
                if (memor == null)
                {
                    throw new VMMException("Unable to find a Managed Entity named : " + darg.MeName + " in Inventory");
                }

                ManagedObjectReference taskmor = cb.SvcConnection.Service.Destroy_Task(memor);

                // If we get a valid task reference, monitor the task for success or failure
                // and report task completion or failure.
                if (taskmor != null)
                {
                    Object[] result = cb.Service.WaitForValues(taskmor,
                       new String[] { "info.state", "info.error" },
                       new String[] { "state" }, // info has a property -                         
                       new Object[][] { new Object[] { TaskInfoState.success, TaskInfoState.error } } //state for state of the task
                    );

                    // Wait till the task completes.
                    res = result[0].Equals(TaskInfoState.success);
                    if (!res)
                    {
                        if (result.Length == 2 && result[1] != null)
                        {
                            if (result[1].GetType().Equals("MethodFault"))
                            {
                                throw new VMMException(actionName, (Exception)result[1]);
                            }
                        }
                    }
                }

                #endregion

                cb.DisConnect();
                return res;
            }
            catch (Exception e)
            {
                throw new VMMException(actionName, e);
            }
        }

        /// <summary>
        /// delete an virtual machine
        /// </summary>
        /// <param name="arg">argument of DeleteVM. the Type should be DeleteVMParameter.</param>
        /// <returns></returns>
        public bool DeleteVM(IVMMParameter arg)
        {
            return Delete("DeleteVM", arg);
        }

        private bool CustomValidation(AppUtil cb, StringBuilder sb)
        {
            bool isvalid = true;
            string op = cb.GetOptionValue("operation");
            if (op.Equals("create"))
            {
                if ((!cb.OptionIsSet("snapshotname")) || (!cb.OptionIsSet("description")))
                {
                    sb.AppendLine("For Create operation SnapshotName"
                                     + " and Description are the Mandatory options");
                    isvalid = false;
                }
            }
            if (op.Equals("remove"))
            {
                if ((!cb.OptionIsSet("snapshotname")) || (!cb.OptionIsSet("removechild")))
                {
                    sb.AppendLine("For Remove operation Snapshotname"
                                      + " and removechild are the Mandatory option");
                    isvalid = false;
                }
                else
                {
                    try
                    {
                        int child = Convert.ToInt32(cb.GetOptionValue("removechild"));

                        if (child != 0 && child != 1)
                        {
                            sb.AppendLine("Value of removechild parameter must be either 0 or 1");
                            isvalid = false;
                        }
                    }
                    catch
                    {
                        sb.AppendLine("Value of removechild parameter must be either 0 or 1");
                        isvalid = false;
                    }
                }
            }
            if (op.Equals("revert"))
            {
                if (!cb.OptionIsSet("snapshotname"))
                {
                    sb.AppendLine("For Revert operation SnapshotName is the Mandatory option");
                    isvalid = false;
                }
            }
            return isvalid;
        }

        private void VMSnapshot(string operation, IVMMParameter arg,
            Action<AppUtil, ManagedObjectReference> done)
        {
            if (!(arg is VMSnapshotParameter))
            {
                throw new ArgumentException("arg is not VMSnapshotParameter type.");
            }
            VMSnapshotParameter sarg = arg as VMSnapshotParameter;
            if (!sarg.IsValid())
            {
                throw new ArgumentException("arg is not valid:" + Environment.NewLine + sarg.ErrorMessage);
            }

            #region userOperations and optionValues

            List<OptionSpec> userOptions = new List<OptionSpec>();
            userOptions.Add(new OptionSpec("vmname", "String", 1, "Name of the virtual machine", null));
            userOptions.Add(new OptionSpec("operation", "String", 1,
                "Type of the operation [list|create|remove|removeall|revert]", null));
            userOptions.Add(new OptionSpec("snapshotname", "String", 0, "Name of Snapshot", null));
            userOptions.Add(new OptionSpec("description", "String", 0, "Description of snapshot", null));
            userOptions.Add(new OptionSpec("removechild", "String", 0,
                "1 if children needs to be removed and 0 if children needn't be removed", null));

            Dictionary<string, object> optionValues = CreateOptions();
            optionValues.Add("vmname", sarg.VMName);
            optionValues.Add("operation", operation);
            if (operation == "create")
            {
                optionValues.Add("description", sarg.Description);
            }
            if (operation == "create" || operation == "revert" || operation == "remove")
            {
                optionValues.Add("snapshotname", sarg.SnapshotName);
            }
            if (operation == "remove")
            {
                optionValues.Add("removechild", sarg.RemoveChildren ? "1" : "0");
            }

            #endregion

            AppUtil cb = AppUtil.Initialize("VMSnapshot", userOptions, optionValues);
            StringBuilder sb = new StringBuilder();
            if (!CustomValidation(cb, sb))
            {
                throw new VMMException(sb.ToString());
            }
            cb.Connect();
            string vmName = cb.GetOptionValue("vmname");
            ManagedObjectReference mor = cb.Service.GetDecendentMoRef(null, "VirtualMachine", vmName);
            if (mor == null)
            {
                throw new VMMException("No VM " + vmName + " found.");
            }
            done(cb, mor);
            cb.DisConnect();
        }

        /// <summary>
        /// create an virtual machine snapshot.
        /// </summary>
        /// <param name="arg">argument of CreateSnapshot. the Type should be VMSnapshotParameter.</param>
        /// <returns></returns>
        public bool CreateSnapshot(IVMMParameter arg)
        {
            try
            {
                bool res = false;
                VMSnapshot("create", arg, (cb, vmor) =>
                {
                    string ssName = cb.GetOptionValue("snapshotname");
                    string desc = cb.GetOptionValue("description");
                    ManagedObjectReference tmor = cb.SvcConnection.Service
                        .CreateSnapshot_Task(vmor, ssName, desc, false, false);
                    res = cb.Service.WaitForTask(tmor) == RESULT_SUCESS;
                });
                return res;
            }
            catch (Exception e)
            {
                throw new VMMException("CreateSnapshot", e);
            }
        }

        private void TraverseSnapshotInTree(AppUtil cb, VirtualMachineSnapshotTree[] snapTree,
            List<IVMSnapshotReference> container)
        {
            if (snapTree == null || snapTree.Length == 0)
            {
                return;
            }
            foreach (var node in snapTree)
            {
                VMWareSnapshotReference snapRef = new VMWareSnapshotReference();
                snapRef.Name = node.name;
                snapRef.CreateTime = node.createTime;
                snapRef.Description = node.description;
                snapRef.Source = node;
                snapRef.VMReference = GetVMObject(cb, node.vm);
                container.Add(snapRef);

                VirtualMachineSnapshotTree[] childTree = node.childSnapshotList;
                TraverseSnapshotInTree(cb, childTree, container);
            }
        }

        private VMWareVMReference GetVMObject(AppUtil cb, ManagedObjectReference vmmor)
        {
            VMWareVMReference vmobj = new VMWareVMReference();

            #region obsolete codes
            //var config = (VirtualMachineConfigInfo)cb.Service.GetDynamicProperty(vmmor, "config");
            //vmRef.VMName = config.name;
            //vmRef.InstanceUUID = config.uuid;
            //vmRef.GuestOsId = config.guestId;
            //vmRef.GuestOsName = config.guestFullName;
            //vmRef.ModifiedTime = config.modified;
            //vmRef.IsTemplate = config.template;
            //var files = (VirtualMachineFileInfo)cb.Service.GetDynamicProperty(vmmor, "files");
            //if (files != null)
            //{
            //    vmRef.FilePath = files.vmPathName;
            //}
            //var guest = (GuestInfo)cb.Service.GetDynamicProperty(vmmor, "guest");
            //if (guest != null)
            //{
            //    vmRef.GuestOsFamily = guest.guestFamily;
            //    vmRef.IP = guest.ipAddress;
            //    vmRef.HostName = guest.hostName;
            //    if (guest.net != null && guest.net.Length > 0)
            //    {
            //        var network = guest.net.FirstOrDefault(nic => nic.ipAddress.Any(ip => ip == guest.ipAddress));
            //        if (network != null)
            //        {
            //            vmRef.Mac = network.macAddress;
            //        }
            //    }
            //    if (guest.disk != null && guest.disk.Length > 0)
            //    {
            //        vmRef.DiskSize = guest.disk.Sum(d => d.capacity);
            //    }
            //}
            //if (config.hardware != null)
            //{
            //    vmRef.CPUCount = config.hardware.numCPU;
            //    vmRef.MemorySize = config.hardware.memoryMB;
            //}
            #endregion

            PropertySpec vmPropSpec = new PropertySpec();

            vmPropSpec.type = "VirtualMachine";

            vmPropSpec.pathSet = new String[] { 

              "name", 
              
              "config.uuid",

              "config.guestId",
              
              "config.guestFullName",

              "config.modified",

              "config.template",

              "config.hardware.memoryMB", 

              "config.hardware.numCPU",

              "config.files",

              "guest.disk",                            

              "guest.ipAddress",

              "guest.hostName",
 
              "guest.guestFamily",

              "guest.net",              

              "runtime.powerState",
            };

            PropertySpec hostPropSpec = new PropertySpec();

            hostPropSpec.type = "HostSystem";

            hostPropSpec.pathSet = new String[] { "name" };

            PropertySpec taskPropSpec = new PropertySpec();

            taskPropSpec.type = "Task";

            taskPropSpec.pathSet = new String[] { "info.name", "info.completeTime" };

            PropertySpec datastorePropSpec = new PropertySpec();

            datastorePropSpec.type = "Datastore";

            datastorePropSpec.pathSet = new String[] { "info" };

            PropertySpec networkPropSpec = new PropertySpec();

            networkPropSpec.type = "Network";

            networkPropSpec.pathSet = new String[] { "name" };

            TraversalSpec hostTraversalSpec = new TraversalSpec();

            hostTraversalSpec.type = "VirtualMachine";

            hostTraversalSpec.path = "runtime.host";

            TraversalSpec taskTravesalSpec = new TraversalSpec();

            taskTravesalSpec.type = "VirtualMachine";

            taskTravesalSpec.path = "recentTask";

            TraversalSpec datastoreTraversalSpec = new TraversalSpec();

            datastoreTraversalSpec.type = "VirtualMachine";

            datastoreTraversalSpec.path = "datastore";

            TraversalSpec networkTraversalSpec = new TraversalSpec();

            networkTraversalSpec.type = "VirtualMachine";

            networkTraversalSpec.path = "network";

            ObjectSpec oSpec = new ObjectSpec();

            oSpec.obj = vmmor;

            PropertyFilterSpec pfSpec = new PropertyFilterSpec();

            pfSpec.propSet = new PropertySpec[] { 
                vmPropSpec, 

                hostPropSpec,

                taskPropSpec, 

                datastorePropSpec, 

                networkPropSpec
            };
            pfSpec.objectSet = new ObjectSpec[] { oSpec };

            ObjectContent[] ocs = cb.SvcConnection.Service
                .RetrieveProperties(cb.SvcConnection.ServiceContent.propertyCollector,
                    new PropertyFilterSpec[] { pfSpec });
            if (ocs != null && ocs.Length > 0)
            {
                foreach (var oc in ocs.Where(o => o.obj.type == "VirtualMachine"))
                {

                    DynamicProperty[] dps = oc.propSet;
                    if (dps != null)
                    {
                        foreach (var dp in dps)
                        {
                            if (dp.name == "name")
                            {
                                vmobj.VMName = dp.val.ToString();
                            }
                            else if (dp.name == "config.uuid")
                            {
                                vmobj.InstanceUUID = dp.val.ToString();
                            }
                            else if (dp.name == "config.guestId")
                            {
                                vmobj.GuestOsId = dp.val.ToString();
                            }
                            else if (dp.name == "config.guestFullName")
                            {
                                vmobj.GuestOsName = dp.val.ToString();
                            }
                            else if (dp.name == "config.modified")
                            {
                                vmobj.ModifiedTime = Convert.ToDateTime(dp.val);
                            }
                            else if (dp.name == "config.template")
                            {
                                vmobj.IsTemplate = Convert.ToBoolean(dp.val);
                            }
                            else if (dp.name == "config.hardware.memoryMB")
                            {
                                vmobj.MemoryMB = Convert.ToInt32(dp.val);
                            }
                            else if (dp.name == "config.hardware.numCPU")
                            {
                                vmobj.CPUCount = Convert.ToInt32(dp.val);
                            }                            
                            else if (dp.name == "config.files")
                            {
                                VirtualMachineFileInfo vmf = (VirtualMachineFileInfo)dp.val;
                                if (vmf != null)
                                {
                                    vmobj.FilePath = vmf.vmPathName;
                                }
                            }
                            else if (dp.name == "guest.disk")
                            {
                                GuestDiskInfo[] gdis = (GuestDiskInfo[])dp.val;
                                if (gdis != null && gdis.Length > 0)
                                {
                                    vmobj.DiskGB = (int)(gdis.Sum(gdi => gdi.capacity) / Math.Pow(1024, 3));
                                }
                            }
                            else if (dp.name == "guest.ipAddress")
                            {
                                vmobj.PrimaryIPv4 = dp.val.ToString();
                            }
                            else if (dp.name == "guest.hostName")
                            {
                                vmobj.HostName = dp.val.ToString();
                            }
                            else if (dp.name == "guest.guestFamily")
                            {
                                vmobj.GuestOsFamily = dp.val.ToString();
                            }
                            else if (dp.name == "guest.net")
                            {
                                GuestNicInfo[] gnis = (GuestNicInfo[])dp.val;
                                if (gnis != null && gnis.Length > 0)
                                {
                                    GuestNicInfo gni = gnis.SingleOrDefault(n => n.ipAddress.Any(ip => ip == vmobj.PrimaryIPv4));
                                    if (gni != null)
                                    {
                                        vmobj.PrimaryMac = gni.macAddress;
                                    }
                                }
                            }
                            else if (dp.name == "runtime.powerState")
                            {
                                vmobj.PowerState = (VMPowerState)dp.val;
                            }
                        }
                    }
                }
            }
            return vmobj;
        }

        private ManagedObjectReference TraverseSnapshotInTree(AppUtil cb, VirtualMachineSnapshotTree[] snapTree,
            string snapName)
        {
            ManagedObjectReference snapmor = null;
            if (snapTree == null || snapTree.Length == 0)
            {
                return snapmor;
            }
            foreach (var node in snapTree)
            {
                if (node.name == snapName)
                {
                    snapmor = node.snapshot;
                }
                else
                {
                    VirtualMachineSnapshotTree[] childTree = node.childSnapshotList;
                    snapmor = TraverseSnapshotInTree(cb, childTree, snapName);
                }
                if (snapmor != null)
                {
                    break;
                }
            }
            return snapmor;
        }

        /// <summary>
        /// list virtual machine's snapshots.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public List<IVMSnapshotReference> ListSnapshot(IVMMParameter arg)
        {
            try
            {
                List<IVMSnapshotReference> snapshots = new List<IVMSnapshotReference>();
                VMSnapshot("list", arg, (cb, vmor) =>
                {
                    ObjectContent[] snaps = cb.Service.GetObjectProperties(null, vmor,
                        new String[] { "snapshot" });
                    VirtualMachineSnapshotInfo snapInfo = null;
                    if (snaps != null && snaps.Length > 0)
                    {
                        ObjectContent snapobj = snaps[0];
                        DynamicProperty[] snapary = snapobj.propSet;
                        if (snapary != null && snapary.Length > 0)
                        {
                            snapInfo = ((VirtualMachineSnapshotInfo)(snapary[0]).val);
                            VirtualMachineSnapshotTree[] snapTree = snapInfo.rootSnapshotList;
                            TraverseSnapshotInTree(cb, snapTree, snapshots);
                        }
                    }
                });
                return snapshots;
            }
            catch (Exception e)
            {
                throw new VMMException("ListSnapshot", e);
            }
        }

        private VirtualMachineSnapshotInfo GetSnapshotInfo(AppUtil cb, ManagedObjectReference vmmor,
            string vmName)
        {
            ObjectContent[] snaps = cb.Service.GetObjectProperties(null, vmmor,
                new String[] { "snapshot" });

            VirtualMachineSnapshotInfo snapInfo = null;
            if (snaps != null && snaps.Length > 0)
            {
                ObjectContent snapobj = snaps[0];
                DynamicProperty[] snapary = snapobj.propSet;
                if (snapary != null && snapary.Length > 0)
                {
                    snapInfo = ((VirtualMachineSnapshotInfo)(snapary[0]).val);
                }
            }
            return snapInfo;
        }

        private ManagedObjectReference GetSnapshotMor(AppUtil cb, ManagedObjectReference vmmor,
            string vmName, string snapName)
        {
            VirtualMachineSnapshotInfo snapInfo = GetSnapshotInfo(cb, vmmor, vmName);
            ManagedObjectReference snapmor = null;
            if (snapInfo != null)
            {
                snapmor = TraverseSnapshotInTree(cb, snapInfo.rootSnapshotList, snapName);
            }
            return snapmor;
        }

        /// <summary>
        /// revert virtual machine from an snapshot.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool RevertSnapshot(IVMMParameter arg)
        {
            try
            {
                bool res = false;
                VMSnapshot("revert", arg, (cb, vmor) =>
                {
                    string vmName = cb.GetOptionValue("vmname");
                    string ssName = cb.GetOptionValue("snapshotname");
                    ManagedObjectReference smor = GetSnapshotMor(cb, vmor, vmName, ssName);
                    if (smor != null)
                    {
                        ManagedObjectReference taskMor = cb.SvcConnection.Service
                            .RevertToSnapshot_Task(smor, null);
                        res = cb.Service.WaitForTask(taskMor) == RESULT_SUCESS;
                    }
                });
                return res;
            }
            catch (Exception e)
            {
                throw new VMMException("RevertSnapshot", e);
            }
        }

        /// <summary>
        /// remove an vitrual machine's snapshot.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool RemoveSnapshot(IVMMParameter arg)
        {
            try
            {
                bool res = false;
                VMSnapshot("remove", arg, (cb, vmor) =>
                {
                    string vmName = cb.GetOptionValue("vmname");
                    string ssName = cb.GetOptionValue("snapshotname");
                    int rem = int.Parse(cb.GetOptionValue("removechild"));
                    ManagedObjectReference smor = GetSnapshotMor(cb, vmor, vmName, ssName);
                    if (smor != null)
                    {
                        ManagedObjectReference taskMor = cb.SvcConnection.Service
                            .RemoveSnapshot_Task(smor, rem == 0 ? false : true);
                        res = cb.Service.WaitForTask(taskMor) == RESULT_SUCESS;
                    }
                });
                return res;
            }
            catch (Exception e)
            {
                throw new VMMException("RemoveSnapshot", e);
            }
        }

        /// <summary>
        /// remove all virtual machine's snapshot.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public bool ClearSnapshot(IVMMParameter arg)
        {
            try
            {
                bool res = false;
                VMSnapshot("removeall", arg, (cb, vmor) =>
                {
                    ManagedObjectReference taskMor = cb.SvcConnection.Service
                        .RemoveAllSnapshots_Task(vmor);
                    res = cb.Service.WaitForTask(taskMor) == RESULT_SUCESS;
                });
                return res;
            }
            catch (Exception e)
            {
                throw new VMMException("ClearSnapshot", e);
            }
        }

        /// <summary>
        /// get an virtual machine from datacenter.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public IVMReference FindVM(IVMMParameter arg)
        {
            try
            {
                if (!(arg is FindVMParameter))
                {
                    throw new ArgumentException("arg is not FindVMParameter type.");
                }
                FindVMParameter farg = arg as FindVMParameter;
                if (!farg.IsValid())
                {
                    throw new ArgumentException("arg is not valid:" + Environment.NewLine + farg.ErrorMessage);
                }
                List<OptionSpec> userOptions = new List<OptionSpec>();
                userOptions.Add(new OptionSpec("vmname", "String", 1, "Name of Virtual Machine", null));
                userOptions.Add(new OptionSpec("DatacenterName", "String", 1, "Name of the Datacenter", null));
                Dictionary<string, object> optionValues = CreateOptions();
                optionValues.Add("vmname", farg.VMName);
                optionValues.Add("DatacenterName", farg.DataCenterName);
                AppUtil cb = AppUtil.Initialize("FindVM", userOptions, optionValues);
                cb.Connect();
                // Find the Datacenter reference by using findByInventoryPath().
                string dcName = cb.GetOptionValue("DatacenterName");
                ManagedObjectReference dcMor = cb.SvcConnection.Service
                    .FindByInventoryPath(cb.SvcConnection.ServiceContent.searchIndex, dcName);
                if (dcMor == null)
                {
                    throw new VMMException("Datacenter:\"" + dcName + "\" not found.");
                }
                //get all vm from datacenter(include template)
                string vmName = cb.GetOptionValue("vmname");
                var vmmor = cb.Service.GetDecendentMoRef(dcMor, "VirtualMachine", vmName);
                if (vmmor == null)
                {
                    throw new VMMException("VM:\"" + vmName + "\" not found.");
                }
                
                VMWareVMReference vmobj = GetVMObject(cb, vmmor);
                cb.DisConnect();
                return vmobj;
            }
            catch (Exception e)
            {
                throw new VMMException("FindVM", e);
            }
        }

        /// <summary>
        /// get all vm from datacenter
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public List<IVMReference> ListVM(IVMMParameter arg)
        {
            try
            {
                if (!(arg is ListVMParameter))
                {
                    throw new ArgumentException("arg is not ListVMParameter type.");
                }
                ListVMParameter larg = arg as ListVMParameter;
                if (!larg.IsValid())
                {
                    throw new ArgumentException("arg is not valid:" + Environment.NewLine + larg.ErrorMessage);
                }
                List<OptionSpec> userOptions = new List<OptionSpec>();
                userOptions.Add(new OptionSpec("DatacenterName", "String", 1, "Name of the Datacenter", null));
                Dictionary<string, object> optionValues = CreateOptions();
                optionValues.Add("DatacenterName", larg.DataCenterName);
                AppUtil cb = AppUtil.Initialize("ListVM", userOptions, optionValues);
                cb.Connect();
                // Find the Datacenter reference by using findByInventoryPath().
                string dcName = cb.GetOptionValue("DatacenterName");
                ManagedObjectReference dcMor = cb.SvcConnection.Service
                    .FindByInventoryPath(cb.SvcConnection.ServiceContent.searchIndex, dcName);
                if (dcMor == null)
                {
                    throw new VMMException("Datacenter:\"" + dcName + "\" not found.");
                }
                //get all vm from datacenter(include template)
                var allVMs = cb.Service.GetDecendentMoRefs(dcMor, "VirtualMachine");
                if (allVMs == null || allVMs.Count == 0)
                {
                    return null;
                }
                List<IVMReference> vmList = new List<IVMReference>();

                foreach (var vmmor in allVMs.Keys)
                {                    
                    VMWareVMReference vmobj = GetVMObject(cb, vmmor);
                    if (vmobj != null)
                    {
                        bool vmIsTemplate = vmobj.IsTemplate ?? false;
                        if (!vmIsTemplate || larg.IncludeTemplate)
                        {
                            vmList.Add(vmobj);
                        }                        
                    }
                }
                cb.DisConnect();
                return vmList;
            }
            catch (Exception e)
            {
                throw new VMMException("ListVM", e);
            }
        }

        /// <summary>
        /// get all template vm from datacenter.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public List<IVMReference> ListTemplateVM(IVMMParameter arg)
        {
            try
            {
                if (!(arg is ListTemplateVMParameter))
                {
                    throw new ArgumentException("arg is not ListTemplateVMParameter type.");
                }
                ListTemplateVMParameter larg = arg as ListTemplateVMParameter;
                if (!larg.IsValid())
                {
                    throw new ArgumentException("arg is not valid:" + Environment.NewLine + larg.ErrorMessage);
                }
                List<OptionSpec> userOptions = new List<OptionSpec>();
                userOptions.Add(new OptionSpec("DatacenterName", "String", 1, "Name of the Datacenter", null));
                Dictionary<string, object> optionValues = CreateOptions();
                optionValues.Add("DatacenterName", larg.DataCenterName);
                AppUtil cb = AppUtil.Initialize("ListTemplateVM", userOptions, optionValues);
                cb.Connect();
                // Find the Datacenter reference by using findByInventoryPath().
                string dcName = cb.GetOptionValue("DatacenterName");
                ManagedObjectReference dcMor = cb.SvcConnection.Service
                    .FindByInventoryPath(cb.SvcConnection.ServiceContent.searchIndex, dcName);
                if (dcMor == null)
                {
                    throw new VMMException("Datacenter:\"" + dcName + "\" not found.");
                }
                //get all vm from datacenter(include template)
                var allVMs = cb.Service.GetDecendentMoRefs(dcMor, "VirtualMachine");
                if (allVMs == null || allVMs.Count == 0)
                {
                    return null;
                }
                List<IVMReference> tempList = new List<IVMReference>();

                foreach (var vmmor in allVMs.Keys)
                {
                    VMWareVMReference vmobj = GetVMObject(cb, vmmor);
                    if (vmobj != null)
                    {
                        bool vmIsTemplate = vmobj.IsTemplate ?? false;
                        if (vmIsTemplate)
                        {
                            tempList.Add(vmobj);
                        }
                    }
                }
                cb.DisConnect();
                return tempList;
            }
            catch (Exception e)
            {
                throw new VMMException("ListVM", e);
            }
        }
    }
}
